#pragma once
#include <stdint.h>

// 键盘处理的应用层定义
// 定义TM1638模块的键盘(有些模块有16个按键，但为了简便起见，定义全部32个按键数量，TM1638实际最多支持24个按键)
#define KEY_NUM 32                          // 1. 总键数，通用起见，这个不能改。如果需要必须把TM1638的四个键盘寄存器压缩到适合的数据里面。
#define SCAN_PERIOD_MS 100                  // 任务内读键周期
#define LONG_TICKS (1000 / SCAN_PERIOD_MS)  // 长按的计数次数
#define REPEAT_TICKS (200 / SCAN_PERIOD_MS) // 重复的计数次数
#define KEY_BUF_SIZE 16                     // 用于键盘事件环形队列，该值决定了环缓的单元数量为KEY_BUF_SIZE，实际可用的数量为KEY_BUF_SIZE -1。
#define DEBOUNCE_MS 100                     // 按键抖动时间ms
#define LONG_PRESS 1000                     // 长按时间ms

// 键盘事件类型枚举：并非TM1638必须，这是应用层类型
typedef enum
{
    KEY_EV_INIT = 0,
    KEY_EV_PRESS,
    KEY_EV_RELEASE,
    KEY_EV_LONG,   // 500 ms
    KEY_EV_REPEAT, // 之后每 200 ms
} key_event_type_t;

// 键盘事件类型
typedef struct
{
    uint8_t key_id;        // 0..n
    key_event_type_t type; // 按键状态
    uint32_t timeStamp;    // 时间戳
} key_event_t;

class TM1638;

class KeyBoard
{
public:
    explicit KeyBoard(TM1638 &tm);

    void scan();

    /**
     * @brief 提供外部用于绑定回调
     *
     * @param f 用户自己的回调
     */
    inline void attach(void (*f)(key_event_t)) { cb = f; }

    /**
     * @brief 获取稳定的键值
     *
     * @return uint32_t
     */
    inline uint32_t getRaw() const { return m_stableRaw; }

    bool getKeyEvent(key_event_t *ev);

private:
    // 键盘事件环形缓冲区
    key_event_t m_keyFifo[KEY_BUF_SIZE] = {{.key_id = 0, .type = KEY_EV_INIT, .timeStamp = 0}};
    uint8_t m_keyWi = 0; // key_fifo的写索引
    uint8_t m_keyRi = 0; // key_fifo的读索引

    TM1638 &m_tm1638;
    uint32_t m_keyRaw;            // 读键的原始值
    uint32_t m_lastRaw = 0;       // 用于检测边沿
    uint32_t m_debounceStart = 0; // 抖动检测开始时间。
    uint32_t m_stableRaw = 0;     // 按键稳定值
    // uint32_t m_pressed = 0;       // 按下的键值
    // uint32_t m_released = 0;      // 释放的键值

    /**
     * @brief 键盘事件回调
     *
     * @param stableRaw  键值
     * @param raisePulse 上升沿
     * @param fallPulse 下降沿
     */
    void (*cb)(key_event_t ev) = nullptr; // 事件回调原型
};